// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//

#include "muduo/net/Buffer.h"

#include "muduo/net/SocketsOps.h"

#include <errno.h>
#include <sys/uio.h>

using namespace muduo;
using namespace muduo::net;

const char Buffer::kCRLF[] = "\r\n";

const size_t Buffer::kCheapPrepend;
const size_t Buffer::kInitialSize;

/**
 * 从文件描述符fd中读取数据，尝试将数据读入到缓冲区中。
 * @param fd 文件描述符，表示要读取数据的来源。
 * @param savedErrno 用于保存错误码的指针，当读取操作失败时，会将errno的值保存到此处。
 * @return 返回实际读取的字节数。如果读取失败，返回-1，并通过savedErrno保存错误码。
 */
ssize_t Buffer::readFd(int fd, int* savedErrno)
{
    // 额外的缓冲区用于存放超过当前缓冲区可写空间的数据
    // saved an ioctl()/FIONREAD call to tell how much to read
    char extrabuf[65536];

    // 定义两个iov用于readv调用，一个指向当前缓冲区的可写空间，一个指向extrabuf
    struct iovec vec[2];

    // 获取当前缓冲区的可写空间大小
    const size_t writable = writableBytes();

    // 设置第一个iov，指向当前缓冲区的可写空间，长度为可写空间大小
    vec[0].iov_base = begin() + writerIndex_;
    vec[0].iov_len  = writable;

    // 设置第二个iov，指向extrabuf，长度为extrabuf的大小
    vec[1].iov_base = extrabuf;
    vec[1].iov_len  = sizeof extrabuf;

    // 根据可写空间大小决定readv调用时的iov数量，如果可写空间小于extrabuf大小，则使用两个iov
    // when there is enough space in this buffer, don't read into extrabuf.
    // when extrabuf is used, we read 128k-1 bytes at most.
    const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;

    // 调用readv函数，尝试读取数据
    const ssize_t n = sockets::readv(fd, vec, iovcnt);

    // 如果读取失败，保存errno到savedErrno
    if (n < 0) {
        *savedErrno = errno;
    } else if (implicit_cast<size_t>(n) <= writable) {
        // 如果读取的数据量小于等于可写空间大小，将数据写入到当前缓冲区的可写空间内
        writerIndex_ += n;
    } else {
        // 如果读取的数据量超过了当前缓冲区的可写空间大小，将超出部分的数据写入到extrabuf中
        // 并更新writerIndex_为缓冲区的末尾位置，然后将extrabuf中的数据追加到缓冲区尾部
        writerIndex_ = buffer_.size();
        append(extrabuf, n - writable);  // 更新buffer
    }

    // 返回实际读取的字节数
    // if (n == writable + sizeof extrabuf)
    // {
    //   goto line_30;
    // }
    return n;
}
